16. Exercise: Adding a ViewModel

L6 31 Add A ViewModel SC

Update Note:
At timestamp 03:00 in the video above, the onCreateView() function of SleepTrackerFragment.kt,…

  1. demonstrates a now deprecated class ViewModelProviders as

    val sleepTrackerViewModel = ViewModelProviders.of( this, viewModelFactory).get(SleepTrackerViewModel::class.java).

    Instead, please use the ViewModelProvider as shown in the instructions below.

  2. The way to set the binding lifecycle to itself has changed from binding.setLifecycleOwner(this) to binding.lifecycleOwner = this.

Now it’s your turn to complete this exercise yourself.

In this step, you'll look at the SleepTrackerViewModel and SleepTrackerViewModelFactory classes, then create a view model in the SleepTrackerFragment and add it to data binding and the layout.

In SleepTrackerViewModel

  1. In the sleeptracker package, open SleepTrackerViewModel.kt.

  2. Inspect the provided SleepTrackerViewModel class definition that extends AndroidViewModel().

    This class is the same as ViewModel, but it takes the application context as a parameter and makes it available as a property. You are going to need this later on to access resources.

    The ViewModel needs access to the data in the database, so pass in an instance of the SleepDatabaseDao. And then pass this up to the superclass as well:

class SleepTrackerViewModel(
       val database: SleepDatabaseDao,
       application: Application) : AndroidViewModel(application) {
}

In SleepTrackerViewModelFactory

  1. In the sleeptracker package, open SleepTrackerViewModelFactory.kt.

    The provided SleepTrackerViewModelFactory takes the same argument as the ViewModel and extends ViewModelProvider.Factory.

    Inside the factory, the code overrides create(), which takes any class type as an argument and returns a ViewModel.

  2. In the body of create(), we check that there is a SleepTrackerViewModel class available, and if there is, return an instance of it. Otherwise, we throw an exception.

    Tip: This is mostly boilerplate code, so it looks very similar to what you created in the previous lesson, and you can reuse it for future view model factories.

SleepTrackerFragment

  1. In the SleepTrackerFragment, in onCreateView(), get a reference to the application context.
val application = requireNotNull(this.activity).application
  1. Define a dataSource.

    To get a reference to the DAO of the database, use SleepDatabase.getInstance(application).sleepDatabaseDao.

val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
  1. Create an instance of the viewModelFactory.

    You'll need to pass in dataSource as well as application.

val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
  1. Get a reference to the SleepTrackerViewModel.

    To the ViewModelProvider, specify to use the viewModelFactory and get an instance of SleepTrackerViewModel::class.java.

val sleepTrackerViewModel =
       ViewModelProvider(
               this, viewModelFactory).get(SleepTrackerViewModel::class.java)
  1. Your finished code in the SleepTrackerFragment should look like this:
 // Create an instance of the ViewModel Factory.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)

 // Get a reference to the ViewModel associated with this fragment.
val sleepTrackerViewModel =
       ViewModelProvider(
               this, viewModelFactory).get(SleepTrackerViewModel::class.java)

Data Binding for ViewModel

  1. In SleepTrackerFragment inside onCreateView():

    Set the current activity as the lifecycle owner of the binding.

binding.lifecycleOwner = this
  1. In fragment_sleep_tracker.xml:

    Inside the <data> block, create a <variable> that references the SleepTrackerViewModel class.

 <data>
   <variable
       name="sleepTrackerViewModel"
       type="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerViewModel" />
</data>
  1. In SleepTrackerFragment inside onCreateView():

    Assign the sleepTrackerViewModel binding variable to the sleepTrackerViewModel.

binding.sleepTrackerViewModel = sleepTrackerViewModel
  1. Finally, as always, make sure your code builds and runs without errors.

If you want to start at this step, you can download this exercise from: Step.04-Exercise-Add-ViewModel.

You will find plenty of //TODO comments to help you complete this exercise, and if you get stuck, go back and watch the video again.

Once you’re done, you can check your solution against the solution we’ve provided here: Step.04-Solution-Add-ViewModel, or using this git diff.

Task List:

Task Feedback:

Great!

Reference documentation